package com.nicole.reqresp.filters;

import com.alibaba.fastjson.JSONObject;
import com.nicole.reqresp.ReqRespProperties;
import com.nicole.reqresp.enums.ExceptField;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * @author  xiaoying
 */
@Slf4j
public class ReqRespParamHelper {

    private ReqRespProperties reqRespProperties;

    public ReqRespParamHelper(ReqRespProperties reqRespProperties) {
        this.reqRespProperties = reqRespProperties;
    }

    public void requestParamLogPrint(HttpServletRequest myRequestBodyReaderWrapper, HttpServletRequest request){
        try {
            List<ExceptField> field =  filterRequestField(reqRespProperties);
            StringBuilder stringBuilder = new StringBuilder();
            for (ExceptField s : field) {
                setParam(s == ExceptField.PATH,(p)-> stringBuilder.append(String.format("访问路径-> %s ",request.getRequestURI())));
                setParam(s == ExceptField.METHOD,(p)-> stringBuilder.append(String.format("访问方式-> %s ",request.getMethod())));
                setParam(s == ExceptField.INPUT_PARAM,(p)-> {
                    try {
                        stringBuilder.append(cutLength(reqRespProperties.getReqCutLength(),String.format("入参-> %s ",allRequestParam(myRequestBodyReaderWrapper,request))));
                    } catch (IOException e) {
                        log.error("入参打印失败");
                    }
                });
                setParam(s == ExceptField.IP,(p)-> stringBuilder.append(String.format("访问者ip-> %s ",RequestUtils.getRealIP(request))));
            }
            log.info(stringBuilder.toString());
        }catch (Exception e){
            log.error("构建入参信息打印失败:{}",e.getMessage());
        }
    }

    public void responseParamLogPrint(MyResponseWrapper responseWrapper,HttpServletRequest request,Long dateStart,Long dateEnd){
        try {
            List<ExceptField> field =  filterRequestField(reqRespProperties);
            StringBuilder stringBuilder = new StringBuilder();
            for (ExceptField s : field) {
                setParam(s == ExceptField.PATH,(p)-> stringBuilder.append(String.format("访问路径-> %s ",request.getRequestURI())));
                setParam(s == ExceptField.OUTPUT_PARAM,(p)-> stringBuilder.append(cutLength(reqRespProperties.getRespCutLength(),String.format("出参-> %s ",allResponseParam(responseWrapper)))));
                setParam(s == ExceptField.TIME_CONSUMING,(p)-> stringBuilder.append(String.format("耗时-> %sms",(dateEnd - dateStart))));
                setParam(s == ExceptField.TIME_LEVEL,(p)-> stringBuilder.append(String.format("耗时级别[%s]",getLevel((dateEnd - dateStart)))));
            }
            log.info(stringBuilder.toString());
        }catch (Exception e){
            log.error("构建出参打印失败:{}",e.getMessage());
        }
    }

    private Integer getLevel(long l) {
        if(l<1000){
            return 0;
        }else if(l>=1000L && l<3000L){
            return 1;
        }else if(l>=3000L && l<5000L){
            return 2;
        }else if(l>=5000L && l<8000L){
            return 3;
        }else if(l>=8000L && l<10000L){
            return 4;
        }else{
            return 5;
        }
    }

    private String allResponseParam(MyResponseWrapper responseWrapper) {
        String responseBody = null;
        if (responseWrapper.getMyOutputStream() == null){
            if (responseWrapper.getMyWriter() != null){
                responseBody = responseWrapper.getMyWriter().getContent();
                responseWrapper.getMyWriter().myFlush();
            }
        }else {
            responseBody = responseWrapper.getMyOutputStream().getBuffer();
            responseWrapper.getMyOutputStream().myFlush();
        }
        return responseBody;
    }


    private <T> void setParam(boolean b, Consumer<T> append) {
        if(b){
            append.accept(null);
        }
    }
    private String cutLength(Integer reqCutLength, String toString) {
        if(null == reqCutLength){
            return toString;
        }
        return toString.length()>reqCutLength?toString.substring(0,reqCutLength):toString;
    }

    private List<ExceptField> filterRequestField(ReqRespProperties reqRespProperties) {
        List<ExceptField> exceptFields = new ArrayList<>(Arrays.asList(ExceptField.values()));
        List<String> exceptFiled = reqRespProperties.getExceptFiled();
        if(null == exceptFiled || exceptFiled.size() == 0){
            return exceptFields;
        }
        return  exceptFields.stream().filter(p->!exceptFiled.contains(p.name())).collect(Collectors.toList());
    }

    private String allRequestParam(HttpServletRequest myRequestBodyReaderWrapper, HttpServletRequest request) throws IOException {
        //所有请求参数的Map
        Map<String,String> paramMap = new HashMap<>();

        //是否拦截并包装请求，如果需要拦截则会获取RequestBody，一般为application/json才拦截
        boolean filterRequestFlag = checkFilter(request.getContentType());
        if (filterRequestFlag) {
            request = new MyRequestBodyReaderWrapper(request);
        }
        //获取所有queryString和requestBody
        Map<String, String> requestParamMap = RequestUtils.getRequestParamMap(myRequestBodyReaderWrapper,request);
        if (requestParamMap != null && !requestParamMap.isEmpty()){
            paramMap.putAll(requestParamMap);
        }
        //获取路径参数
        Map<String,String> uriTemplateMap = RequestUtils.getUriTemplateVar(request);
        if (uriTemplateMap != null && !uriTemplateMap.isEmpty()){
            paramMap.putAll(uriTemplateMap);
        }
        return JSONObject.toJSONString(paramMap);
    }

    /**
     * 判断请求/返回是否为application/json
     * 是则进行拦截，
     * 否则退出
     * @param contentType 请求/响应类型
     */
    private boolean checkFilter(String contentType) {
        //是否继续拦截
        boolean filterFlag = false;
        for (String p : getContentTypes()) {
            if (StringUtils.contains(contentType, p)) {
                filterFlag = true;
                break;
            }
        }
        if (StringUtils.isEmpty(contentType)){
            filterFlag = true;
        }
        return filterFlag;
    }
    private String[] getContentTypes() {
        return new String[]{"application/json"};
    }
}
